package com.piece.generator.controller;

import com.piece.core.db.util.DataBaseUtil;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.annotation.permission.AclPermission;
import com.piece.core.framework.annotation.permission.SubmitPermission;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.basic.ConfigUtil;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.web.controller.FrameWorkController;
import com.piece.module.generator.model.GenTable;
import com.piece.module.generator.model.GenTableColumn;
import com.piece.module.generator.model.TbSysDb;
import com.piece.module.generator.model.TbSysValidate;
import com.piece.module.generator.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@RefreshScope
@RestController
@Api(tags = {"代码自动生成管理"})
@RequestMapping("/api/system/generator")
public class GenController extends FrameWorkController {

    @Value(value = "${schema.gen.install:false}")
    private boolean enable;

    @Resource
    private GenService genService;

    @Resource
    private GenTableService genTableService;

    @Resource
    private GenTableColumnService genTableColumnService;

    @Resource
    private TbSysDbService tbSysDbService;

    @Resource
    private TbSysValidateService tbSysValidateService;

    /**
     * 初始化生成业务表数据
     */
    @PostConstruct
    public void initGenTable() {
        if (enable) {
            TbSysDb tbSysDb = new TbSysDb();
            tbSysDb.setDbKey(FrameWorkConstants.DATASOURCE_MASTER);
            tbSysDb.setDriverClassName(ConfigUtil.getInstance().get("spring.datasource.druid.master.driver-class-name"));
            genService.generateTableColumn(null, null);
        }
    }

    @SubmitPermission
    @PostMapping("/init")
    @AclPermission(code = "tool:gen:init")
    @ApiOperation(value = "初始化业务表", notes = "初始化业务表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "dbKey", value = "数据源"),
            @ApiImplicitParam(name = "tableNames", value = "数据表名称数组")
    })
    public AjaxResponse init() {
        try {
            String dbKey = getRequest().getParameter("dbKey");
            TbSysDb tbSysDb = tbSysDbService.findByDBKey(dbKey);
            if (null == tbSysDb) {
                tbSysDb = new TbSysDb();
                tbSysDb.setDbKey(FrameWorkConstants.DATASOURCE_MASTER);
                tbSysDb.setDriverClassName(ConfigUtil.getInstance().get("spring.datasource.druid.master.driver-class-name"));
            }

            String tableNames = getRequest().getParameter("tableNames");
            if (null != tableNames && tableNames.length() > 0) {
                String[] tableName = tableNames.split(FrameWorkConstants.SPLIT_PARAM);
                TbSysDb finalTbSysDb = tbSysDb;
                Arrays.stream(tableNames.split(FrameWorkConstants.SPLIT_PARAM)).forEach(name -> {
                    genService.generateTableColumn(finalTbSysDb, name);
                });
            } else {
                genService.generateTableColumn(tbSysDb, null);
            }
            return success();
        } finally {
            DataBaseUtil.clearDataSource();
        }
    }

    @PostMapping("/list")
    @AclPermission(code = "tool:gen:view")
    @ApiOperation(value = "查询业务表列表")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public List list() {
        QueryTable queryTable = initTable();
        DataTable dataTable = genTableService.findPage(queryTable);
        return dataTable.getData();
    }

    @PostMapping("/table")
    @AclPermission(code = "tool:gen:view")
    @ApiOperation(value = "查询业务表分页数据")
    @ApiImplicitParam(value = "查询条件", dataTypeClass = QueryTable.class, type = "body")
    public AjaxResponse table() {
        QueryTable queryTable = initTable();
        DataTable dataTable = genTableService.findPage(queryTable);
        return success(dataTable);
    }

    @GetMapping("/loadTable/{tableId}")
    @AclPermission(code = "tool:gen:view")
    @ApiOperation(value = "加载业务表单数据")
    @ApiImplicitParam(name = "tableId", value = "业务表主键", type = "path")
    public AjaxResponse load(@PathVariable String tableId) {
        GenTable genTable = genTableService.findById(Convert.toLong(tableId));
        return success(genTable);
    }

    @PostMapping("/loadColumn/{tableId}")
    @AclPermission(code = "tool:gen:view")
    @ApiOperation(value = "加载数据列数据")
    @ApiImplicitParam(name = "tableId", value = "业务表主键", type = "path")
    public AjaxResponse loadColumnTable(@PathVariable String tableId) {
        GenTable genTable = genTableService.findById(Convert.toLong(tableId));
        List<GenTableColumn> columns = new ArrayList<>();
        if (genTable != null) {
            columns = genTableColumnService.findByTableId(Convert.toLong(tableId));
        }
        DataTable dataTable = new DataTable();
        dataTable.setRecordsTotal(columns.size());
        dataTable.setData(columns);
        return success(dataTable);
    }

    @SubmitPermission
    @PostMapping("/saveColumn/{tableId}")
    @AclPermission(code = "tool:gen:edit")
    @ApiOperation(value = "保存数据列数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tableId", value = "表单列ID", type = "path"),
            @ApiImplicitParam(name = "columnIndex", value = "验证规则个数"),
            @ApiImplicitParam(value = "表单数据", dataTypeClass = GenTable.class, type = "body")
    })
    public AjaxResponse saveColumn(@PathVariable Long tableId) {
        try {
            GenTable genTable = genTableService.findById(tableId);
            initDB(genTable);
            genTableService.insertOrUpdate(genTable);
            // 加载验证规则
            int index = Convert.toInt(getRequest().getParameter("columnIndex"), 0);
            if (index > 0) {
                List<GenTableColumn> columns = new ArrayList<>();
                GenTableColumn genTableColumn;
                Long columnId;
                for (int i = 1; i <= index; i++) {
                    columnId = Convert.toLong(getRequest().getParameter("columns[" + i + "].columnId"), 0L);
                    genTableColumn = genTableColumnService.findById(columnId);
                    if (genTableColumn != null) {
                        initDB(genTableColumn, "columns[" + i + "].");
                        genTableColumn.setSort(i);
                        columns.add(genTableColumn);
                    }
                }

                if (columns.size() > 0) {
                    genTableColumnService.batchUpdate(columns);
                }
            }
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS), null);
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @GetMapping("/loadRule/{columnId}")
    @AclPermission(code = "tool:gen:view")
    @ApiOperation(value = "加载业务表属性验证规则")
    @ApiImplicitParam(name = "columnId", value = "主键", type = "path")
    public AjaxResponse loadRule(@PathVariable Long columnId) {
        GenTableColumn genTableColumn = genTableColumnService.findById(columnId);
        if (null != genTableColumn) {
            GenTable genTable = genTableService.findById(genTableColumn.getTableId());
            genTableColumn.setTableName(genTable.getTableName());
            List<TbSysValidate> validates = tbSysValidateService.findByColumnId(columnId);
            genTableColumn.setValidates(validates);
        }
        return success(genTableColumn);
    }

    @SubmitPermission
    @PostMapping("/saveRule")
    @AclPermission(code = "tool:gen:edit")
    @ApiOperation(value = "保存业务表属性验证规则")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ruleIndex", value = "验证规则个数"),
            @ApiImplicitParam(value = "表单数据", dataTypeClass = GenTableColumn.class, type = "body")
    })
    public AjaxResponse saveRule() {
        try {
            GenTableColumn column = new GenTableColumn();
            List<TbSysValidate> validates = new ArrayList<>();
            initDB(column);

            // 加载验证规则
            int index = Convert.toInt(getRequest().getParameter("ruleIndex"), 0);
            if (index > 0) {
                String validateName = null, validateRule = null;
                TbSysValidate validate = null;
                for (int i = 1; i <= index; i++) {
                    validateName = getRequest().getParameter("validateName" + i);
                    validateRule = getRequest().getParameter("validateRule" + i);
                    if (StringUtil.isNotEmpty(validateName)) {
                        validate = new TbSysValidate();
                        validate.setTableName(column.getTableName());
                        validate.setColumnName(column.getColumnName());
                        validate.setColumnId(column.getColumnId());
                        validate.setValidateName(validateName);
                        validate.setRules(validateRule);
                        validates.add(validate);
                    }
                }
            }
            tbSysValidateService.deleteByColumnId(column.getColumnId());
            tbSysValidateService.flush();
            tbSysValidateService.batchInsert(validates);
            return success(I18nUtil.message(ExceptionConstants.SAVE_SUCCESS), null);
        } catch (Exception e) {
            return error(I18nUtil.message(ExceptionConstants.SAVE_FAILURE), e.getMessage());
        }
    }

    @SubmitPermission
    @GetMapping("/previewCode/{tableId}")
    @AclPermission(code = "tool:gen:export")
    @ApiOperation(value = "预览代码")
    @ApiImplicitParam(name = "tableId", value = "tableId", type = "path")
    public AjaxResponse previewCode(@PathVariable String tableId) {
        GenTable table = genTableService.findById(Convert.toLong(tableId));
        table.setColumns(genTableColumnService.findByTableId(table.getTableId()));
        Map<String, String> codes = genService.generatorCode(table, null);
        return success(codes.entrySet().stream().map(entry -> {
            Map<String, String> param = new HashMap<>();
            param.put("path", entry.getKey());
            param.put("code", entry.getValue());
            return param;
        }).collect(Collectors.toList()));
    }

    /**
     * 生成代码
     */
    @SubmitPermission
    @GetMapping("/genCode")
    @AclPermission(code = "tool:gen:export")
    @ApiOperation(value = "生成代码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tableId", value = "业务表主键"),
            @ApiImplicitParam(name = "type", value = "生成类别", allowableValues = "java、html、vue")
    })
    public void genCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Long[] tableIds = Convert.toLongArray(FrameWorkConstants.SPLIT_PARAM, request.getParameter("tableId"));
        String type = request.getParameter("type");
        byte[] data = genService.generatorCode(tableIds, type);
        genCode(response, data);
    }

    /**
     * 生成zip文件
     */
    private void genCode(HttpServletResponse response, byte[] data) throws IOException {
        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"code.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }
}
